{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit MediaLocations;

interface

uses Classes, XmlHelper, MediaFolders;

const
  MEDIALOC_FILENAME = 'Media.xml';
  MY_MUSIC = 'My Music';
  MY_VIDEO = 'My Videos';
  MY_IMAGE = 'My Pictures';

type
  TAHMMediaLocationParser = class
  private
    FTrailSlash: Boolean;
    FImageFolders: TAHMMediaFolderList;
    FVideoFolders: TAHMMediaFolderList;
    FMusicFolders: TAHMMediaFolderList;
    FParseNode: TAHMXMLNode;
    FHelper: TAHMXMLHelper;
    procedure ParseMediaLocations;
  protected
    function GetFolder(Index: TAHMMediaFolderType): TAHMMediaFolderList;
    procedure LogParsingError(const sErrorCode: String);
  public
    constructor Create;
    destructor Destroy; override;
    property TrailingSlashes: Boolean read FTrailSlash write FTrailSlash;
    property ImageFolders: TAHMMediaFolderList read FImageFolders;
    property VideoFolders: TAHMMediaFolderList read FVideoFolders;
    property MusicFolders: TAHMMediaFolderList read FMusicFolders;
    property Folders[Index: TAHMMediaFolderType]: TAHMMediaFolderList read GetFolder;
    procedure ClearFolders;
    procedure AppendDefaultFolders;
    procedure AppendExternalFolders;
    function ParseXML(const Filepath: String): Boolean;
    procedure SaveAsXML(Lines: TStrings);
  end;


implementation

uses SysUtils, StrUtils, AHMTypes, AHMConsts, TagNames, XMLTags, ErrorCodes,
     Utilities;

constructor TAHMMediaLocationParser.Create;
begin
  inherited Create;

  FImageFolders := TAHMMediaFolderList.Create;
  FVideoFolders := TAHMMediaFolderList.Create;
  FMusicFolders := TAHMMediaFolderList.Create;
end;

destructor TAHMMediaLocationParser.Destroy;
begin
  FImageFolders.Free;
  FVideoFolders.Free;
  FMusicFolders.Free;

  inherited Destroy;
end;

function TAHMMediaLocationParser.GetFolder(Index: TAHMMediaFolderType): TAHMMediaFolderList;
begin
  case Index of
    ftMusic: Result := FMusicFolders;
    ftVideo: Result := FVideoFolders;
    ftImage: Result := FImageFolders;
    else Result := nil;
  end;
end;

procedure TAHMMediaLocationParser.ClearFolders;
begin
  // Clear existing media locations in case we are re-parsing
  FImageFolders.Clear;
  FVideoFolders.Clear;
  FMusicFolders.Clear;
end;

procedure TAHMMediaLocationParser.AppendDefaultFolders;
var
  Music, Video, Image: String;
begin
  // Add default folders for My Music, My Videos and My Pictures
  Music := GetWindowsPath(wpMyMusic);
  Video := GetWindowsPath(wpMyVideos);
  Image := GetWindowsPath(wpMyPictures);

  // Ensure location & name have a trailing slash if required
  if FTrailSlash then
  begin
    FMusicFolders.AddFolder(MY_MUSIC + PathDelim, Music + PathDelim, False);
    FVideoFolders.AddFolder(MY_VIDEO + PathDelim, Video + PathDelim, False);
    FImageFolders.AddFolder(MY_IMAGE + PathDelim, Image + PathDelim, False);
  end
  else
  begin
    FMusicFolders.AddFolder(MY_MUSIC, Music, False);
    FVideoFolders.AddFolder(MY_VIDEO, Video, False);
    FImageFolders.AddFolder(MY_IMAGE, Image, False);
  end;
end;

procedure TAHMMediaLocationParser.AppendExternalFolders;
var
  i: Integer;
  Drives: TStringList;
  sName, sLocn: String;
begin
  // Add default media locations for removable media devices
  Drives := TStringList.Create;
  try
    GetDrives(Drives, [drvOptical]);
    for i := 0 to Pred(Drives.Count) do
      if TAHMDriveType(Drives.Objects[i]) = drvOptical then
      begin
        sName := 'DVD (' + Drives[i] + ')';
        sLocn := Drives[i] + PATH_DVD;

        // Ensure location & name have a trailing slash if required
        if FTrailSlash then sLocn := sLocn + PathDelim;
        if FTrailSlash then sName := sName + PathDelim;

        // Add this location to our list of video folders
        FVideoFolders.AddFolder(sName, sLocn, False, False);
      end;
  finally
    Drives.Free;
  end;
end;

function TAHMMediaLocationParser.ParseXML(const Filepath: String): Boolean;
var
  RootNode: TAHMXMLNode;
begin
  // Default result - problem loading Media locations configuration
  Result := False;

  // Don't bother trying to parse XML file if its missing
  if not FileExists(FilePath + MEDIALOC_FILENAME) then
  begin
    // Add default folders instead
    AppendDefaultFolders;
    Exit;
  end;

  FHelper := TAHMXMLHelper.Create(Filepath + MEDIALOC_FILENAME);
  try
    // Ignore preamble - we're looking for a MediaManager node
    if not FHelper.FindRootElement(RootNode, TAG_MEDIA_MANAGER) then
      LogParsingError(XML_PRS_MLC_MISSING_MEDIA_MGR_START_TAG);

    // What nodes do we have here?
    FParseNode := nil;
    while FHelper.NextElement(RootNode, FParseNode) do
    begin
      if FHelper.CurrentElement = TAG_MEDIA_LOCATIONS then
        ParseMediaLocations
      else
        LogParsingError(XML_PRS_MLC_FOUND_UNKNOWN_CONTENT_TAG);
    end;

    // All is well, parsed media locations file successfully
    Result := True;
  finally
    FHelper.Free;
  end;
end;

procedure TAHMMediaLocationParser.SaveAsXML(Lines: TStrings);
begin
  Lines.Add(XML_PREAMBLE);
  Lines.Add(BeginTag(TAG_MEDIA_MANAGER));
  Lines.Add(TABS[1] + BeginTag(TAG_MEDIA_LOCATIONS));

  // Add each of our media lists
  Lines.Add(TABS[2] + BeginTag(TAG_MEDIA_MUSIC_LOCNS));
  FMusicFolders.SaveAsXML(Lines);
  Lines.Add(TABS[2] + EndTag(TAG_MEDIA_MUSIC_LOCNS));
  Lines.Add(TABS[2] + BeginTag(TAG_MEDIA_VIDEO_LOCNS));
  FVideoFolders.SaveAsXML(Lines);
  Lines.Add(TABS[2] + EndTag(TAG_MEDIA_VIDEO_LOCNS));
  Lines.Add(TABS[2] + BeginTag(TAG_MEDIA_PICTURE_LOCNS));
  FImageFolders.SaveAsXML(Lines);
  Lines.Add(TABS[2] + EndTag(TAG_MEDIA_PICTURE_LOCNS));

  Lines.Add(TABS[1] + EndTag(TAG_MEDIA_LOCATIONS));
  Lines.Add(EndTag(TAG_MEDIA_MANAGER));
end;

procedure TAHMMediaLocationParser.ParseMediaLocations;
var
  sName, sLocn: String;
  lVirtual: Boolean;
  mType: TAHMMediaType;
  MediaNode, DirNode: TAHMXMLNode;
begin
  // What media types do we have here?
  MediaNode := nil;
  while FHelper.NextElement(FParseNode, MediaNode) do
  begin
    // Determine media type
    if FHelper.CurrentElement = TAG_MEDIA_MUSIC_LOCNS then mType := mtAudio
    else if FHelper.CurrentElement = TAG_MEDIA_VIDEO_LOCNS then mType := mtVideo
    else if FHelper.CurrentElement = TAG_MEDIA_PICTURE_LOCNS then mType := mtImage
    else mType := mtUnknown;

    DirNode := nil;

    // Keep going as long as we find directories to process
    while FHelper.FindElement(MediaNode, DirNode, TAG_MEDIA_DIRECTORY) do
    begin
      // Get directory logical name & physical location
      sName := FHelper.GetAttribute(ATTR_NAME);
      lVirtual := Str2Bool(FHelper.GetAttribute(ATTR_VIRTUAL));
      sLocn := FHelper.GetValue;

      // Ensure location & name have a trailing slash if required
      if FTrailSlash then
      begin
        if RightStr(sLocn, 1) <> PathDelim then sLocn := sLocn + PathDelim;
        if RightStr(sName, 1) <> PathDelim then sName := sName + PathDelim;
      end;

      // Add location to list of media folders for appropriate media type
      case mType of
        mtAudio: FMusicFolders.AddFolder(sName, sLocn, lVirtual);
        mtVideo: FVideoFolders.AddFolder(sName, sLocn, lVirtual);
        mtImage: FImageFolders.AddFolder(sName, sLocn, lVirtual);
      end;
    end;
  end;
end;

procedure TAHMMediaLocationParser.LogParsingError(const sErrorCode: String);
begin
  // Raise exception for global error handler...
  raise EConfigurationException.Create('Encountered error whilst parsing Media Locations XML: ' + sErrorCode);
end;

end.
